package pers.cz.filter;

import cn.hutool.core.date.DateUtil;
import pers.cz.enums.CommonCode;
import pers.cz.io.IOUtils;
import pers.cz.mapper.PostgirlLogMapper;
import pers.cz.model.Postgirl;
import pers.cz.model.PostgirlLog;
import pers.cz.pub.RespBean;
import pers.cz.spring.SpringContextUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.springframework.security.authentication.*;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;
import org.springframework.security.web.util.matcher.RequestMatcher;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collection;
import java.util.Date;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @program: springSecurity
 * @description: JwtFilter
 * @author: Cheng Zhi
 * @create: 2021-04-16 10:39
 **/
public class JwtLoginFilter extends AbstractAuthenticationProcessingFilter {

    private static final Long EXPIRATION = 60 * 60 * 3 * 1000L; //过期时间3小时,这里单位为ms
    // private static final Long EXPIRATION = 30 * 1000L; //过期时间3小时,这里单位为ms
    private static final String SIGNINGKEY = "PostGirl_Token_Public"; // 签名
    private String defaultFilterProcessesUrl;
    private Jedis jedis;

    protected JwtLoginFilter(String defaultFilterProcessesUrl) {
        super(defaultFilterProcessesUrl);
    }

    protected JwtLoginFilter(RequestMatcher requiresAuthenticationRequestMatcher) {
        super(requiresAuthenticationRequestMatcher);
    }

    public JwtLoginFilter(String defaultFilterProcessesUrl, AuthenticationManager authenticationManager, JedisPool jedisPool) {

        super(defaultFilterProcessesUrl, authenticationManager);
        this.defaultFilterProcessesUrl = defaultFilterProcessesUrl;
        this.jedis = jedisPool.getResource();
    }

    protected JwtLoginFilter(RequestMatcher requiresAuthenticationRequestMatcher, AuthenticationManager authenticationManager) {
        super(requiresAuthenticationRequestMatcher, authenticationManager);
    }

    /**
     * 校验登录
     * @param httpServletRequest
     * @param httpServletResponse
     * @return
     * @throws AuthenticationException
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws AuthenticationException, IOException, ServletException {

        Postgirl user = new ObjectMapper().readValue(httpServletRequest.getInputStream(), Postgirl.class);
        if (user.getVerifyCode() == null || user.getVerifyCode().equals("")) {
            throw new AuthenticationServiceException("验证码不能为空!");
        }
        // 从redis中查询code
        String uuid = httpServletRequest.getHeader("uuid");
        String value = jedis.get(CommonCode.VERIFYPRI + uuid);
        if (!user.getVerifyCode().equalsIgnoreCase(value)) {
            throw new AuthenticationServiceException("验证码错误!");
        }
        // 异步校验
        String remoteAddr = httpServletRequest.getRemoteAddr(); // 获取登录ip地址
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        CompletableFuture.runAsync(() ->{
            try{
                recordLoginUser(user.getUserName(), remoteAddr, "");
            }catch (Exception e){
                logger.error("记录用户登录信息失败", e);
            }
        },executorService);
        executorService.shutdown(); // 回收线程池
        return getAuthenticationManager().authenticate(new UsernamePasswordAuthenticationToken(user.getUserName(),user.getPassWord()));
    }

    /**
     * 记录用户登录日志
     * @param userName
     * @param clientAddress
     * @param clientName
     */
    private void recordLoginUser(String userName, String clientAddress, String clientName) {

        PostgirlLog postgirlLog = new PostgirlLog();
        postgirlLog.setClientAddress(clientAddress);
        postgirlLog.setClientName(clientName);
        postgirlLog.setUserName(userName);
        postgirlLog.setLoginTime(DateUtil.date());

        SpringContextUtil.getBean(PostgirlLogMapper.class).insertSelective(postgirlLog);
    }

    @Override
    public void afterPropertiesSet() {
        super.afterPropertiesSet();
    }

    /**
     * 登录成功
     * @param request
     * @param response
     * @param chain
     * @param authResult
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        // authResult登录成功的用户信息
        Collection<? extends GrantedAuthority> authorities = authResult.getAuthorities(); // 获取登录用户的角色
        Postgirl user = (Postgirl) authResult.getPrincipal(); // 获取登录用户信息
        StringBuffer sb = new StringBuffer();
        for (GrantedAuthority authority : authorities) {
            sb.append(authority.getAuthority()).append(",");
        }
        String userId = user.getUserId().toString();
        String jwt = Jwts.builder().claim("authorities",sb)
                .setSubject(userId)
                .setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
                .signWith(SignatureAlgorithm.HS512,SIGNINGKEY)
                .compact();
        RespBean respBean = RespBean.ok("登录成功", user, jwt);
        response.setContentType("application/json;charset=utf-8");
        PrintWriter out = response.getWriter();
        out.write(new ObjectMapper().writeValueAsString(respBean));
        out.flush();
        IOUtils.close(out);
    }

    /**
     * 登录失败
     * @param request
     * @param response
     * @param exception
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {

        logger.debug("-------错误请求的地址------" + request.getRequestURL());
        RespBean respBean = RespBean.error(exception.getMessage());
        if (exception instanceof LockedException) {
            respBean.setMsg("账户被锁定，请联系管理员!");
        } else if (exception instanceof CredentialsExpiredException) {
            respBean.setMsg("密码过期，请联系管理员!");
        } else if (exception instanceof AccountExpiredException) {
            respBean.setMsg("账户过期，请联系管理员!");
        } else if (exception instanceof DisabledException) {
            respBean.setMsg("账户被禁用，请联系管理员!");
        } else if (exception instanceof BadCredentialsException) {
            respBean.setMsg("用户名或者密码输入错误，请重新输入!");
        } else if (exception instanceof AuthenticationServiceException) {
            respBean.setMsg(exception.getMessage());
        } else {
            respBean.setMsg("登录失败");
        }
        response.setContentType("application/json;charset=utf-8");
        PrintWriter out = response.getWriter();
        out.write(new ObjectMapper().writeValueAsString(respBean));
        out.flush();
        IOUtils.close(out);
    }
}
